<?php
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'wsr.inc.php';
require_once WSR_DIR . 'activationparams.class.php';
require_once WSR_DIR . 'axgraph.class.php';

class Activation {   // Alan writes
    var $initial_activation_levels; // assoc array entity -> number
    var $activation_levels;         // assoc array entity -> number
	var $params;
	var $debug = false;
	var $debug_trace;
	/*
	 * all now in params
	var $load_threshold = 5;
	var $load_fanout_threshold = 1;
	var $spread_factor = 0.1;
	var $sigmoid_table;
	*/
	var $in;         // used during calculations
	protected $meta;      //   ""    ""
	protected $graph;      //   ""    ""
	protected $inv_graph;  //   ""    ""
    function __construct($params=false) {
		$this->set_params($params);
		$this->set_meta();
	}
    function set_params($params=false) {
		if ( ! $params ) $params = new ActivationParams();
		$this->params = $params;
	}
    function get_params() {
		return $this->params;
	}
    function set_meta($meta=false) {
		if ( ! $meta ) $meta = array( 'fan-out'=>array(), 'fan-in'=>array(), 'avg-fan-out'=>array(), 'avg-fan-in'=>array() ); 
		$this->meta = $meta;
	}
    function set_trace($debug_trace=false) {
		$this->debug_trace = $debug_trace;
	}
	/*
     * @param array entities list of entities
	 * initialisation stuff
     * @param integer level an initial activation level for them all (0-100)
	 */
	function set_initial_activated_entities($entities,$level=100) {
		$this->initial_activation_levels = array();
		foreach( $entities as $e ) {
			$this->initial_activation_levels[$e] = $level;
		}
		$this->activation_levels = $this->initial_activation_levels;
	}
	/*
	 * initialisation stuff
	 * alternatie to set_initial_activated_entities givng more control
     * @param array initial_activation_levels  an associative array mapping entities to theor initial activation
	 */
	function set_initial_activation($initial_activation_levels) {
		$this->initial_activation_levels = initial_activation_levels;
		$this->activation_levels = $this->initial_activation_levels;
	}
	/*
	 * initialisation stuff
     * @param AxGraph graph the graph of triples to work on
	 */
	function set_graph(AxGraph $graph) {
		$this->graph = $graph->make_resource_graph();
		$this->inv_graph = $graph->make_inverse_graph();
		
		//$t = $graph->get_triples();
		//echo "set_graph(graph) graph has " . count($t) . " triples \n";
	
		//$t2 = array_slice($t,0,2);
		//print_r($t2); echo "\n\n\n";

		//$t = $this->graph->get_triples();
		//echo "set_graph: this->graph has " . count($t) . " triples \n";
	
		//$t2 = array_slice($t,0,2);
		//print_r($t2); echo "\n\n\n";

	}
	function get_graph() {
		return $this->graph;
	}
	/*
	 * perform a number of spreading activation cycles
     * @param integer n the number of steps to perform
	 */
    function do_steps($n=1) {
		for( $i=0; $i<$n; $i++ ) {
			$this->do_one();
		}
    }
	/*
	 * perform a single spreading activation cycle
	 */
    function do_one() {
		$this->in = $this->initial_activation_levels;
		$this->_compute_in($this->graph,$this->meta['fan-out'],$this->meta['avg-fan-out']);
		$this->_compute_in($this->inv_graph,$this->meta['fan-in'],$this->meta['avg-fan-in']);
		$this->activation_levels = array();
		$this->activation_levels = array_map( array($this->params,'sigmoid'), $this->in );
    }
	/*
	 *  internal helper function
	 *  compares activation with threshold
	 */
	function loadable_activation($act) {
		$res = $act > $this->params->load_threshold;
		//echo "testing $act > $this->load_threshold => $res \n";
		return $res;
	}
	/*
	 *  internal helper function
	 *  computes the fanout needed so that entities related through a prediacte with the fanout will achieve sufficient threshold activation
	 */
	function loadable_max_fanout($act) {
		$necessary_activation = $this->params->inverse_sigmoid($this->params->load_fanout_threshold);
		$fanout = floor( $this->params->spread_factor * $act / $necessary_activation );
		return $fanout;
	}
	/*
	 *  filters the activated entities to sleect those with high enough actvation to be 'loadable'
	 *  the resuting entities need to be diffed with already loaded entities to find new ones to load
	 *  ??? not sure of this actually belongs inside this class, or in an outer class
	 *      but leave for now as coupling bound to increase
	 *  @return array of all loadable entities
	 */
	function get_loadable_entities() {
		return array_keys( array_filter( $this->activation_levels, array( $this, 'loadable_activation' ) ) );
	}
	/*
	 *  for each loadable entity works out the fan out needed for related entities 
	 *  to achieve sufficiently high activation based on this entity alone
	 *  @return array of loadable entities and associated maximum fan out
	 */
	function get_loadable_entities_with_fanout() {
		$loadable = array_filter( $this->activation_levels, array( $this, 'loadable_activation' ) );
		return array_map( array( $this, 'loadable_max_fanout' ), $loadable );
	}
	/*
	 *  internal helper function
	 *  computes incoming activation for nodes forma  graoh
	 *  gets called twice, once for graph and once for inverse
	 */
	function _compute_in(AxGraph $graph,$meta=null,$avg_meta=null) {
		$index = $graph->get_index();
		if ( ! $meta ) $meta = array();
		if ( ! $avg_meta ) $avg_meta = array();
		if ( $this->debug ) echo "meta = " . print_r($meta,true) . "\n";
		foreach( $index as $s => $predicates ) {
			$act = $this->activation_levels[$s];
			//echo "_compute_in: $s => $act\n";
			if ( $act > 0 ) {
				if ( array_key_exists( $s, $meta ) ) {
					$s_meta = $meta[$s];
					if ( $this->debug ) echo "have meta for $s\n";
				} else {
					$s_meta = array();
				}
				//echo "spreading from $s\n";
				$nos_predicates = count($predicates);
				foreach ( $predicates as $p => $objects ) {
					$graph_outdegree = count($objects);
					if ( array_key_exists( $p, $s_meta ) ) {
						$outdegree = $s_meta[$p];
						if ( $this->debug ) echo "have meta for $s $p = $outdegree\n";
					} elseif ( array_key_exists( $p, $avg_meta ) ) {
						$outdegree = $avg_meta[$p];
						if ( $this->debug ) echo "have avgerage meta for $s $p = $outdegree\n";
					} else {
						$outdegree = $graph_outdegree;
					}
					$spread = $this->_calculate_spread($act,$outdegree,$nos_predicates);
					foreach ( $objects as $o_obj ) {
						$o = $o_obj['value'];
						if ( $this->params->use_feedback_control ) {
							$act_diff = $act - $this->activation_levels[$o];
							//echo "    $s -> $o :  $spread * $act_diff / $act = " . ( $spread * $act_diff / $act ) . "\n";
							if ( $this->debug ) echo "    $s ={$p}[$outdegree|$graph_outdegree]=> $o :  spread=$spread  act_diff=$act_diff act=$act \n";
							if ( $act_diff > 0 ) {
								//$this->in[$o] += $spread * $act_diff / $act ;
								$this->in[$o] += $spread;
							}
						} else {
							$this->in[$o] += $spread;
						}
						//echo "  spreading $spread to $o[value]\n";
					}
				}
			}
		}
	}
	/*
	 *  internal helper function
	 *  works out how much activation t pass down a link
	 */
	function _calculate_spread($act,$outdegree,$nos_predicates) {
		return $this->params->spread_factor * $act / $outdegree;
	}
}
   


?>